# Active Record Migrations

  • Introduction

    Migrations are like version control for your database, allowing your team to define and share the application's database schema definition. If you have ever had to tell a teammate to manually add a column to their local database schema after pulling in your changes from source control, you've faced the problem that database migrations solve.

  • Generating Migrations

    You may use the bin/rails generate migrationrails command to generate a database migration. The new migration will be placed in your **db/migrate**directory. Each migration filename contains a timestamp that allows Laravel to determine the order of the migrations:

    bin/rails generate migration AddPartNumberToProducts
    
    1

    This will create an appropriately named empty migration:

    class AddPartNumberToProducts < ActiveRecord::Migration[7.0]
      def change
      end
    end
    
    1
    2
    3
    4

    Rials will use the name of the migration to attempt to guess the name of the table and whether or not the migration will be creating a new table. If Rails is able to determine the table name from the migration name, Rails will pre-fill the generated migration file with the specified table. Otherwise, you may simply specify the table in the migration file manually.

    If the migration name is of the form "AddColumnToTable" or "RemoveColumnFromTable" and is followed by a list of column names and types then a migration containing the appropriate [add_column](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_column)and [remove_column](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-remove_column)statements will be created.

    bin/rails generate migration AddPartNumberToProducts part_number:string
    
    1

    will generate

    class AddPartNumberToProducts < ActiveRecord::Migration[7.0]
      def change
        add_column :products, :part_number, :string
      end
    end
    
    1
    2
    3
    4
    5
  • Migration Structure

    Sometimes we see the method name is upand downand sometimes we see the method name is changein the migration file.

    class AddPartNumberToProducts < ActiveRecord::Migration[7.0]
      def change
      end
    end
    
    1
    2
    3
    4

    You can also use the old style of migration using upand downmethods instead of the changemethod. The upmethod should describe the transformation you'd like to make to your schema, and the downmethod of your migration should revert the transformations done by the upmethod.

    class ExampleMigration < ActiveRecord::Migration[7.0]
      def up
        create_table :distributors do |t|
          t.string :zipcode
        end
      end
    
      def down
        drop_table :distributors
      end
    end
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • Running Migration

    To run all of your outstanding migrations, execute the migrate Artisan command:

    bin/rails db:migrate
    
    1

    If you would like to see which migrations have run thus far, you may use the migrate:status rails command:

    bin/rails db:migrate:status
    
    1

    If you specify a target version, Active Record will run the required migrations (change, up, down) until it has reached the specified version. The version is the numerical prefix on the migration's filename. For example, to migrate to version 20080906120000 run:

    bin/rails db:migrate VERSION=20080906120000
    
    1

    If version 20080906120000 is greater than the current version (i.e., it is migrating upwards), this will run the change(or up) method on all migrations up to and including 20080906120000, and will not execute any later migrations. If migrating downwards, this will run the downmethod on all the migrations down to, but not including, 20080906120000.

    By default running bin/rails db:migratewill run in the developmentenvironment. To run migrations against another environment you can specify it using the RAILS_ENVenvironment variable while running the command. For example to run migrations against the test environment you could run:

    bin/rails db:migrate RAILS_ENV=test
    
    1

    Rolling Back

    bin/rails db:rollback
    
    bin/rails db:rollback STEP=3
    
    #shortcut for doing a rollback and then migrating back up again.
    bin/rails db:migrate:redo STEP=3
    
    1
    2
    3
    4
    5
    6
  • Table

    • Creating Table

      create_table :products do |t|
        t.string :name
      end
      
      1
      2
      3

      which creates a products table with a column called name.

      By default, create_table will create a primary key called id. You can change the name of the primary key with the :primary_key option or, if you don't want a primary key at all, you can pass the option id: false. If you need to pass database specific options you can place an SQL fragment in the :options option. For example:

      create_table :products, options: "ENGINE=BLACKHOLE" do |t|
        t.string :name, null: false
      end
      
      1
      2
      3

      will append ENGINE=BLACKHOLE to the SQL statement used to create the table.

      An index can be created on the columns created within the create_tableblock by passing true or an options hash to the :indexoption:

      create_table :users do |t|
        t.string :name, index: true
        t.string :email, index: { unique: true, name: 'unique_emails' }
      end
      
      1
      2
      3
      4
    • Creating a Join Table

      create_join_table :products, :categories
      
      1

      which creates a categories_productstable with two columns called category_id and  product_id. These columns have the option :null set to falseby default. This can be overridden by specifying the :column_optionsoption:

      create_join_table :products, :categories, column_options: { null: true }
      
      1

      By default, the name of the join table comes from the union of the first two arguments provided to create_join_table, in alphabetical order. To customize the name of the table, provide a :table_nameoption:

      create_join_table :products, :categories, table_name: :categorization
      
      1

      creates a categorization table.

      create_join_tablealso accepts a block, which you can use to add indices (which are not created by default) or additional columns:

      create_join_table :products, :categories do |t|
        t.index :product_id
        t.index :category_id
      end
      
      1
      2
      3
      4
    • Changing Table

      A close cousin of create_tableis [change_table](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_table), used for changing existing tables. It is used in a similar fashion to create_tablebut the object yielded to the block knows more tricks. For example:

      change_table :products do |t|
        t.remove :description, :name
        t.string :part_number
        t.index :part_number
        t.rename :upccode, :upc_code
      end
      
      1
      2
      3
      4
      5
      6

      removes the descriptionand namecolumns, creates a part_numberstring column and adds an index on it. Finally it renames the upccodecolumn.

    • Changing Columns

      Like the remove_columnand add_columnRails provides the [change_column](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_column)migration method.

      change_column :products, :part_number, :text
      
      1

      This changes the column part_number on products table to be a :text field. Note that change_column command is irreversible.

      Besides change_column,the [change_column_null](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_column_null) and [change_column_default](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_column_default) method are used specifically to change a not null constraint and default values of a column.

      change_column_null :products, :name, false
      change_column_default :products, :approved, from: true, to: false
      
      1
      2

      This sets :namefield on products to a NOT NULLcolumn and the default value of the :approvedfield from true to false.

    • References

      The add_reference method allows the creation of an appropriately named column.

      add_reference :users, :role
      
      1

      This migration will create a role_idcolumn in the users table. It creates an index for this column as well, unless explicitly told not with the index: falseoption:

      add_reference :users, :role, index: false
      
      1

      The method add_belongs_to is an alias of add_reference.

      add_belongs_to :taggings, :taggable, polymorphic: true
      
      1

      The polymorphic option will create two columns on the taggings table which can be used for polymorphic associations: taggable_type and taggable_id.

      A foreign key can be created with the foreign_key option.

      add_reference :users, :role, foreign_key: true
      
      1

      References can also be removed:

      remove_reference :products, :user, foreign_key: true, index: false
      
      1
    • When Helpers aren't Enough

      If the helpers provided by Active Record aren't enough you can use the [execute](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-execute) method to execute arbitrary SQL:

      Product.connection.execute("UPDATE products SET price = 'free' WHERE 1=1")
      
      1